﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DDD.Core {
    /// <summary>
    /// コンポーネントの抽象基底クラス
    /// </summary>
    /// <remarks>
    /// コンポーネントはノードにアタッチされ、ノードに機能を追加します。
    /// すべてのコンポーネントはこの抽象基底クラスを継承して宣言されています。
    /// 単なる3D空間上の点に過ぎなかったノードは、いろいろな機能を持ったコンポーネントをアタッチすることで、
    /// 多様な役割を果たすようになります。
    /// またこのクラスはノードとコンポーネントの基本的な橋渡しなる機能を持ちます。
    /// <para>
    /// <h3>ノード - コンポーネント モデル</h3>
    /// DDDでは <see cref="Node"/> はシーングラフを構成する機能と座標変換のみを持ち、単なる3D空間上の一点を表現します。
    /// カメラやメッシュなどの機能はノードにコンポーネントをアタッチする事で追加されます。
    /// これをノード - コンポーネント モデルと言います。
    /// 通常シーングラフはあとから変更される事は少なく、機能の追加などによって影響を受けるべきではありません。
    /// これらの追加機能をコンポーネントとして分離する事で、柔軟で変更に強いゲーム エンジンとなっています。
    /// 1つのコンポーネント オブジェクトは同時に1つのノードにアタッチされます。
    /// 同時に複数のノードにアタッチする事はできません。
    /// </para>
    /// <note type="implement">
    /// あとですべてのコンポーネントへの参照を追加
    /// </note>
    /// </remarks>
    /// <seealso cref="Node"/>
    public abstract class Component : Object {

        #region Field
        Node node;
        #endregion

        #region Constructor
        /// <summary>
        /// コンストラクター
        /// </summary>
        /// <remarks>
        /// 初期値は以下の通りです。
        /// <list type="bullet">
        ///   <item><see cref="Node"/> = <c>null</c></item>
        /// </list>
        /// どのノードにもアタッチされていません。
        /// </remarks>
        public Component () {
            this.node = null;
        }
        #endregion

        #region Property
        /// <summary>
        /// アタッチされたノード
        /// </summary>
        /// <remarks>
        /// このコンポーネントがアタッチされているノードです。
        /// </remarks>
        public Node Node {
            get { return node; }
        }

        /// <inheritdoc/>
        public override IEnumerable<Object> References {
            get {
                return base.References;
            }
        }
        #endregion

        #region Mehthod

        /// <summary>
        /// アタッチされたノードの変更
        /// </summary>
        /// <remarks>
        /// アタッチされたノードを変更します。
        /// </remarks>
        /// <param name="node">ノード</param>
        internal void SetNode (Node node) {
            this.node = node;
        }

        /// <summary>
        /// メッセージの送信
        /// </summary>
        /// <remarks>
        /// 宛先ノード <paramref name="node"/> にアタッチされたコンポーネントの中で
        /// 指定の名前のメソッド <paramref name="func"/> を持つものがあれば、そのメソッドを呼び出します。
        /// これをメッセージの送信と呼んでいます。
        /// メッセージを受け取るコンポーネント型を指定して送信したい場合は、<see cref="SendMessage{TComp}(Node,string)"/> を使用します。
        /// </remarks>
        /// <param name="node">宛先のノード</param>
        /// <param name="func">メソッドの名前</param>
        /// <seealso cref="SendMessage{TComp}(Node,string)"/>
        public void SendMessage (Node node, string func) {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            if (func == null || func == "") {
                throw new ArgumentNullException ("Method name is null or empty");
            }

            node.RecvMessage (func);
        }

        /// <summary>
        /// メッセージの送信
        /// </summary>
        /// <remarks>
        /// 宛先ノード <paramref name="node"/> にアタッチされたコンポーネントの中で
        /// 指定の名前のメソッド <paramref name="func"/> を持つものがあれば、そのメソッドを呼び出します。
        /// これをメッセージの送信と呼んでいます。
        /// </remarks>
        /// <typeparam name="TArg1">引数1の型</typeparam>
        /// <param name="node">宛先ノード</param>
        /// <param name="func">呼び出すメソッドの名前</param>
        /// <param name="arg1">引数1</param>
        public void SendMessage<TArg1> (Node node, string func, TArg1 arg1) {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            if (func == null || func == "") {
                throw new ArgumentNullException ("Method name is null or empty");
            }

            node.RecvMessage (func, arg1);
        }

        /// <summary>
        /// メッセージの送信
        /// </summary>
        /// <remarks>
        /// 宛先ノード <paramref name="node"/> にアタッチされたコンポーネントの中で
        /// 指定の名前のメソッド <paramref name="func"/> を持つものがあれば、そのメソッドを呼び出します。
        /// これをメッセージの送信と呼んでいます。
        /// </remarks>
        /// <typeparam name="TComp">コンポーネント型</typeparam>
        /// <param name="node">宛先ノード</param>
        /// <param name="func">メソッドの名前</param>
        public void SendMessage<TComp> (Node node, string func) where TComp : Component {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            if (func == null || func == "") {
                throw new ArgumentNullException ("Method name is null or empty");
            }

            node.RecvMessage<TComp> (func);
        }


        /// <summary>
        /// メッセージの送信
        /// </summary>
        /// <remarks>
        /// 宛先ノード <paramref name="node"/> にアタッチされたコンポーネントの中で
        /// 指定の名前のメソッド <paramref name="func"/> を持つものがあれば、そのメソッドを呼び出します。
        /// これをメッセージの送信と呼んでいます。
        /// </remarks>
        /// <typeparam name="TComp">コンポーネント型</typeparam>
        /// <typeparam name="TArg1">引数1の型</typeparam>
        /// <param name="node">宛先ノード</param>
        /// <param name="name">メソッドの名前</param>
        /// <param name="arg1">引数1</param>
        public void SendMessage<TComp, TArg1> (Node node, string name, TArg1 arg1) where TComp : Component {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            if (name == null || name == "") {
                throw new ArgumentNullException ("Method name is null or empty");
            }

            node.RecvMessage<TComp, TArg1> (name, arg1);
        }



        /// <summary>
        /// アップデート処理
        /// </summary>
        /// <remarks>
        /// DDD から呼び出される仮想関数の1つで、コンポーネントのアップデート処理を行います。
        /// コンポーネント派生型はこの仮想関数をオーバーライドして独自のアップデート処理を実装すべきです。
        /// 通常、引数の <see cref="worldTime"/> はワールドの時刻が msec 単位で収納されています。
        /// </remarks>
        /// <param name="worldTime">ワールド時間</param>
        public virtual void OnUpdate (int worldTime) {
            // do nothing
        }

        /// <summary>
        /// リフレッシュ処理
        /// </summary>
        /// <remarks>
        /// DDD から呼び出される仮想関数の1つで、コンポーネントの <c>Uniform</c> 変数を更新します。
        /// コンポーネント派生型はこの仮想関数をオーバーライドして独自のリフレッシュ処理を実装可能です。
        /// 通常は標準のリフレッシュ処理で十分なので、これを実装すべきではありません。
        /// <note type="implement">
        /// 現在このリフレッシュ処理は呼び出されず、
        /// 毎フレームレンダリングの時に <c>Uniform</c> 変数を更新します。
        /// この実装は意図した物ではなく、修正されます。
        /// </note>
        /// <note type="important">
        /// 通常は標準のリフレッシュ処理で十分です。
        /// この仮想関数をユーザーがオーバーライドすべきではありません。
        /// </note>
        /// </remarks>
        public virtual void OnReflesh () {
            throw new NotImplementedException ("Sorry");
        }


        /// <summary>
        /// コリジョン発生処理
        /// </summary>
        /// <remarks>
        /// DDD から呼び出される仮想関数の1つで、コリジョンが発生した時のコンポーネント固有の処理を定義します。
        /// コンポーネント派生型はこの仮想関数をオーバーライドして独自のコリジョン処理を実装可能です。
        /// コリジョン検出は同じノードに <see cref="CollisionVolume"/> コンポーネントがアタッチされていて、
        /// <see cref="Physics3D.Collide"/> メソッドが呼ばれると呼び出される可能性があります。
        /// <see cref="OnCollisionEnter"/> メソッドはコリジョン領域がオーバーラップした最初の1回だけ呼ばれます。
        /// </remarks>
        /// <param name="collisions">コリジョン情報</param>
        /// <seealso cref="OnCollisionExit()"/>
        public virtual void OnCollisionEnter (Collision[] collisions) {
            // do nothing
        }

        /// <summary>
        /// コリジョン解消処理
        /// </summary>
        /// <remarks>
        /// DDD から呼び出される仮想関数の1つで、コリジョンが解消された時のコンポーネント固有の処理を定義します。
        /// このメソッドは原則<see cref="OnCollisionEnter"/>と同じですが、
        /// コリジョン領域のオーバーラップが解消された最初の1回だけ呼ばれます。
        /// </remarks>
        /// <param name="collisions">発生したコリジョン情報</param>
        /// <seealso cref="OnCollisionEnter()"/>
        public virtual void OnCollisionExit (Collision[] collisions) {
            // do nothing
        }

        /// <inheritdoc/>
        public override string ToString () {
            return string.Format ("");
        }

        #endregion
    }
}
